package algorithm.sphericharmonics;

import index.MTreeIndex;
import index.SparseMatrix;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.util.Hashtable;

import viewer.JRoff;
import xxl.core.collections.containers.Container;
import xxl.core.collections.queues.DynamicHeap;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.filters.Taker;
import xxl.core.indexStructures.MTree.Sphere;
import xxl.core.indexStructures.Tree.Query.Candidate;


import model.FineTrianglePixelization;
import model.GeometryModel3D;
import model.IPixelizationListener;
import model.RoffTriangle;
import model.RoffTriangleManager;
import model.RoffVector3D;
import model.TrianglePixelization;

public class VoxelizedSphereManager implements IPixelizationListener {
	private static  float radius_unit=0.01f;
	private static final float PI=(float)Math.PI; 
	private float TWOPI=(float)(2*Math.PI);  
	private float sizeOfPixel; 
	private final Hashtable<Integer,VoxelizedSphere> spheres=new Hashtable<Integer,VoxelizedSphere>(); 
	private float x_center; 
	private float y_center; 
	private float z_center; 
	public VoxelizedSphereManager(float x, float y, float z,float size){
		x_center=x; 
		y_center=y; 
		z_center=z; 
		sizeOfPixel=size; 
	}
	@Override
	public void informs(double x, double y, double z,double weight) {
		float r=(float)Math.sqrt((x-x_center)*(x-x_center)+(y-y_center)*(y-y_center)+(z-z_center)*(z-z_center));
		VoxelizedSphere sphere=getSphere(r);
		float f=(float)(Math.PI/180); 
		// theta is from 0 to PI;  
		// atan2 return value with the range of -pi to pi
		
		
		
		float theta=(float)Math.atan2(Math.sqrt((x-x_center)*(x-x_center)+(y-y_center)*(y-y_center)), x-x_center);
		
		// phi is from 0 to 2*PI; 
		float phi=(float)Math.atan2(y-y_center, x-x_center);
		
		theta=theta+f*60; 
		//phi=phi+f*20;
		
		theta=theta % TWOPI;
		if (theta>PI){
			theta=TWOPI-theta; 
		}
		phi=phi%TWOPI;
		 
		// phi should be from 0 to 2*PI;
		phi=phi>0?phi:TWOPI+phi;
		sphere.addPixel(theta, phi); 
	}
	public ShapeDescriptor transform(){
		ShapeDescriptor desc=new ShapeDescriptor(); 
		for(VoxelizedSphere sph:spheres.values()){
			//if (sph.getSphereIndex()==2)
			{
				//System.out.println("----->sphere:"+sph.getSphereIndex()+" size:"+sph.size() +" r:"+sph.getRadius());
				float[] l2Normal=sph.transform();
				SphereDescriptor sphdesc=new SphereDescriptor(sph.getSphereIndex(),l2Normal);
				desc.addSphereDescriptor(sphdesc); 
			}
		}
		//desc.print(); 
		return desc; 
	}
	private VoxelizedSphere getSphere(float radius){
		int radius_index=(int)(radius/radius_unit); 
		VoxelizedSphere sphere=spheres.get(radius_index); 
		if (sphere==null){
			int numberOfGrid=(int)(PI/calculateDeltaAngle(radius));
			if (numberOfGrid<VoxelizedSphere.lmax){
				numberOfGrid=VoxelizedSphere.lmax; 
			}
			sphere=new VoxelizedSphere(radius,numberOfGrid ,radius_index);
			spheres.put(radius_index, sphere); 
		}
		return sphere; 
	}
	public int size(){
		int size=0; 
		for (VoxelizedSphere s:spheres.values()){ 
			size+=s.size(); 
		}
		return size; 
	}
	// radius*dAngle=sizeOfPixel; 
	private float calculateDeltaAngle(float radius){
		return  sizeOfPixel/radius; 
	}
	private static int count=0; 
	public static void indexFolder(String folder){
		File dir = new File(folder);
	    // It is also possible to filter the list of returned files.
	    // This example does not return any files that start with `.'.
	    FilenameFilter filter = new FilenameFilter() {
	        public boolean accept(File dir, String name) {
	            //return !name.startsWith(".");
	        	//System.out.println("name:"+name); 
	        	return name.endsWith(".off"); 
	        }
	    };
	    File[] off_files = dir.listFiles(filter);
	    if (off_files!=null){
	    	for (int i=0; i<off_files.length; i++){
	    		//System.out.println(off_files[i].getAbsolutePath());
	    		indexOff(off_files[i].getAbsolutePath());
	    		count++; 
	    	}
	    }
	    // The list of files can also be retrieved as File objects
	    //File[] folders = dir.listFiles();
	    
	    // This filter only returns directories
	    FilenameFilter dirFilter = new FilenameFilter() {
	        public boolean accept(File file,String name){  
	            return file.isDirectory();
	        }
	    };
	    File[] folders = dir.listFiles(dirFilter);
	    if (folders!=null){
	    	for (int i=0; i<folders.length; i++){
	    		indexFolder(folders[i].getAbsolutePath());
	    	}
	    }
	}
	public static void testSearch(String fileName){
		try {
			System.out.println("===>"+fileName); 
			//String fileName="/home/cyliu/mobileworkspace/javamesh/src/viewer/asu.off"; 
			BufferedReader in = new BufferedReader(new FileReader(fileName));
			BufferedReader info=new BufferedReader(new FileReader(JRoff.makeInfoName(fileName))); 
			GeometryModel3D model=new GeometryModel3D();
			model.read(in,info); 
			long t0=System.currentTimeMillis(); 
			RoffTriangleManager tmanager=new RoffTriangleManager();
			tmanager.setPointArray(model.vertices); 
			tmanager.generateTrangles(model);
			int size=tmanager.getNumberOfTriangles();
			TrianglePixelization p=new TrianglePixelization();
			RoffVector3D c=model.getCenteroid(); 
			VoxelizedSphereManager pManager=new VoxelizedSphereManager((float)c.x,(float)c.y,(float)c.z,p.sizeOfPixelCube);
			p.setListener(pManager); 
			for (int i=0; i<size; i++){
				RoffTriangle tri=tmanager.get(i); 
				p.doPixelization2D(tri); 
			}
			System.out.println("time:"+(System.currentTimeMillis()-t0)); 
			System.out.println(pManager.spheres.size()+"/"+pManager.size());
			ShapeDescriptor sphd=pManager.transform();
			sphd.objectId=model.modelID; 
			String indexFolder="/media/disk/data";
			t0=System.currentTimeMillis();
			System.out.println("search by ObjectId:"+sphd.objectId);
			MTreeIndex index=new MTreeIndex(indexFolder);
			Container container=index.reopenIndex();
			System.out.println("Tree height:"+index.getMTree().height());
			for (int i=0; i<1; i++){
			
			//index.getMTree().insert(sphd.toSparseMatrix()); 
			//index.commit();
			 
			SparseMatrix m=sphd.toSparseMatrix();
			
			Sphere queryObject=new Sphere(m, 1.0,null);  
			
			// consuming the fifty nearest elements concerning the query object at the
			// the target level;
			// the order is determined by the comparator given to the dynamic heap
			// structure realizing a priority queue
			
			int targetLevel=0;
			
			Cursor cursor = new Taker(
					index.getMTree().query(new DynamicHeap(MTreeIndex.getDistanceBasedComparator(queryObject)), targetLevel),
					10
			);
			while (cursor.hasNext()) {
				Sphere next = (Sphere)((Candidate)cursor.next()).descriptor();
				//System.out.println("candidate no. "+(++counter)+": "+next
				System.out.println("objectId:"+((SparseMatrix)next.center()).getObjectId()+" distance to query object: "+queryObject.centerDistance(next) +" radius:"+next.radius());
			}
			cursor.close();
		
			 
			}
			container.close();
			System.out.println("time:"+(System.currentTimeMillis()-t0));
		}catch (Exception ex){
			ex.printStackTrace();
			System.out.println("problem file:"+fileName); 
			System.exit(0); 
		}
	}
	public static void main(String[] args){
		String fileName="/home/cyliu/mobileworkspace/javamesh/benchmark/db/2/m249/m249.off"; 
		testSearch(fileName);
		//indexAll(); 
	}
	public static void indexAll(){
		long t0=System.currentTimeMillis(); 
		String rootDir="/home/cyliu/mobileworkspace/javamesh/benchmark/db"; 
		indexFolder(rootDir);
		System.out.println("count:"+count); 
		long t=System.currentTimeMillis(); 
		System.out.println("*** time:"+((t-t0)/1000));
		//String off="/home/cyliu/mobileworkspace/javamesh/benchmark/db/17/m1785/m1785.off"; 
		//indexOff(off); 
	}
	public static void indexOff(String fileName){
		try {
			System.out.println("===>"+fileName); 
			//String fileName="/home/cyliu/mobileworkspace/javamesh/src/viewer/asu.off"; 
			BufferedReader in = new BufferedReader(new FileReader(fileName));
			BufferedReader info=new BufferedReader(new FileReader(JRoff.makeInfoName(fileName))); 
			GeometryModel3D model=new GeometryModel3D();
			model.read(in,info); 
			long t0=System.currentTimeMillis(); 
			RoffTriangleManager tmanager=new RoffTriangleManager();
			tmanager.setPointArray(model.vertices); 
			tmanager.generateTrangles(model);
			int size=tmanager.getNumberOfTriangles();
			TrianglePixelization p=new TrianglePixelization();
			RoffVector3D c=model.getCenteroid(); 
			VoxelizedSphereManager pManager=new VoxelizedSphereManager((float)c.x,(float)c.y,(float)c.z,p.sizeOfPixelCube);
			p.setListener(pManager); 
			for (int i=0; i<size; i++){
				RoffTriangle tri=tmanager.get(i); 
				p.doPixelization2D(tri); 
			}
			System.out.println("time:"+(System.currentTimeMillis()-t0)); 
			System.out.println(pManager.spheres.size()+"/"+pManager.size());
			ShapeDescriptor sphd=pManager.transform();
			sphd.objectId=model.modelID; 
			String indexFolder="/media/disk/data"; 
			MTreeIndex index=new MTreeIndex(indexFolder);
			Container container=index.reopenIndex();
			index.getMTree().insert(sphd.toSparseMatrix()); 
			index.commit(); 
			container.close(); 
			System.out.println("time:"+(System.currentTimeMillis()-t0));
		}catch (Exception ex){
			ex.printStackTrace();
			System.out.println("problem file:"+fileName); 
			System.exit(0); 
		}
	}
	public static void test(){
		try {
			//String fileName="/home/cyliu/mobileworkspace/javamesh/src/viewer/asu.off";
			String fileName="/home/cyliu/mobileworkspace/javamesh/benchmark/db/0/m0/m0.off"; 
			BufferedReader in = new BufferedReader(new FileReader(fileName));
			BufferedReader info=new BufferedReader(new FileReader(JRoff.makeInfoName(fileName))); 
			GeometryModel3D model=new GeometryModel3D();
			model.read(in,info); 
			long t0=System.currentTimeMillis(); 
			RoffTriangleManager tmanager=new RoffTriangleManager();
			tmanager.setPointArray(model.vertices); 
			tmanager.generateTrangles(model);
			int size=tmanager.getNumberOfTriangles();
			TrianglePixelization p=new TrianglePixelization();
			RoffVector3D c=model.getCenteroid(); 
			VoxelizedSphereManager pManager=new VoxelizedSphereManager((float)c.x,(float)c.y,(float)c.z,p.sizeOfPixelCube);
			p.setListener(pManager); 
			for (int i=0; i<size; i++){
				RoffTriangle tri=tmanager.get(i); 
				p.doPixelization2D(tri); 
			}
			System.out.println("time:"+(System.currentTimeMillis()-t0)); 
			System.out.println(pManager.spheres.size()+"/"+pManager.size());
			ShapeDescriptor sphd=pManager.transform();
			sphd.objectId=model.modelID; 
			String indexFolder="/media/disk/data"; 
			MTreeIndex index=new MTreeIndex(indexFolder);
			Container container=index.reopenIndex();
			index.getMTree().insert(sphd.toSparseMatrix()); 
			index.commit(); 
			container.close(); 
			System.out.println("time:"+(System.currentTimeMillis()-t0));
		}catch (Exception ex){
			ex.printStackTrace(); 
		}
	}
}
